package zzps.exeed.kernel.core;

import zzps.exeed.kernel.aop.Aspect;
import zzps.exeed.kernel.aop.ProxyFactory;
import zzps.exeed.kernel.aop.asm.AsmProxyFactory;
import zzps.exeed.kernel.awareness.AwarenessProcessor;
import zzps.exeed.kernel.holder.PandaHolder;
import zzps.exeed.kernel.holder.ValueInjector;
import zzps.exeed.kernel.pandabuilder.PandaBuilder;
import zzps.exeed.kernel.pandainjector.PandaInjector;
import zzps.exeed.kernel.tool.AnnotationTool;
import zzps.exeed.kernel.tool.ClassTool;
import zzps.exeed.kernel.tool.StrTool;

import java.io.Closeable;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

public final class PandaContainer {



    private ProxyFactory proxyFactory = new AsmProxyFactory();
    /**
     * 实例建造器
     */
    private final Map<Class<? extends Annotation>, PandaBuilder<? extends Annotation>> pandaBuilders = new ConcurrentHashMap<>();
    /**
     * 实例注入器
     */
    private final Map<Class<? extends Annotation>, PandaInjector<? extends Annotation>> pandaInjectors = new ConcurrentHashMap<>();
    /**
     * 能力的实例处理器
     */
    private final List<AwarenessProcessor> awarenessProcessorList = new ArrayList<>();

    /**
     * 实例定义容器，不包括切面，分开实例化
     */
    private final List<PandaEmbryo<? extends Annotation>> embryoList = new ArrayList<>(1024*3);
    /**
     * 切面实例定义容器，分开来特殊处理
     */
    private final List<PandaEmbryo<? extends Annotation>> aspectEmbryoList = new ArrayList<>(32);
    /**
     * 切面容器
     */
    private final List<PandaHolder<Aspect>> aspectList = new ArrayList<>(32);
    private Set<String> pandaNames = new HashSet<>(1024*3);
    /**
     * 类型和注册的panda名称映射
     */
    private final Map<Class<?>, List<String>> pandaNameByClass = new ConcurrentHashMap<>(1024*10);
    /**
     * 根据名字注册的panda容器
     */
    private final Map<String, PandaHolder> pandaHolderByName = new HashMap<>(1024*3);


    protected final Map<Object, Set<Consumer<PandaHolder>>> pandaSubscribers = new HashMap<>();

    public ProxyFactory getProxyFactory() {
        return proxyFactory;
    }

    //----------------------------------------------------------------添加获取构建器-------------------------------------
    public <T extends Annotation> void addPandaBuilder(Class<T> clazz, PandaBuilder<T> builder) {
        pandaBuilders.put(clazz, builder);
    }
    public <T extends Annotation> PandaBuilder<T> getPandaBuilder(Class<T> clazz){
        return (PandaBuilder<T>) pandaBuilders.get(clazz);
    }
    //----------------------------------------------------------------添加获取构建器-------------------------------------
    //=========================================添加获取注入器=============================================
    public <T extends Annotation> void addPandaInjector(Class<T> clazz,PandaInjector<T> injector){
        pandaInjectors.put(clazz,injector);
    }
    public <T extends Annotation> PandaInjector<T> getPandaInjector(Class<T> clazz){
        return (PandaInjector<T>) pandaInjectors.get(clazz);
    }
    //=========================================添加获取注入器============================================

    public void addAwarenessProcessor(AwarenessProcessor awarenessProcessor){
        awarenessProcessorList.add(awarenessProcessor);
    }

    /**
     * 往容器注册panda
     * @param pandaHolder
     */
    public void registerPanda(PandaHolder pandaHolder){
        if (pandaHolder ==null ||pandaHolder.clazz() == null||pandaHolder.raw() ==null)
            return;
        {
            putPandaHolder(pandaHolder);
            pandaNotice(pandaHolder.clazz(), pandaHolder);
            pandaNotice(pandaHolder.getName(), pandaHolder);
        }

    }
    public <T> PandaHolder<T> getPanda(Object nameOrType){
        if (nameOrType instanceof String) {
            return pandaHolderByName.get(nameOrType);
        }else {
            List<String> names = pandaNameByClass.get(nameOrType);
            if (names == null){
                return null;
            }
            if (names.size() > 1){
                throw new RuntimeException("当前类型有多个panda"+nameOrType);
            }
            if (names.size() == 1){
                return getPanda(names.get(0));
            }
        }
        throw new RuntimeException("param nameOrType 必须是class或是string类型");
    }
    public void getPandaAsync(Object nameOrType,Consumer<PandaHolder> consumer){
        PandaHolder holder = getPanda(nameOrType);
        if (holder == null){
            pandaSubscribe(nameOrType,consumer);
        }else {
            consumer.accept(holder);
        }
    }

    public void injectPanda(ValueInjector valueInjector, String name){
        if (StrTool.isEmpty(name)){
            //类型注入
            if(AppContext.class.isAssignableFrom(valueInjector.getType())){
                valueInjector.setValue(this);
                return;
            }
            getPandaAsync(valueInjector.getType(), pandaHolder -> valueInjector.setValue(pandaHolder.get()));
        }else {
            //按名字注入
            getPandaAsync(name,pandaHolder -> valueInjector.setValue(pandaHolder.get()));
        }
    }
    private void putPandaHolder(PandaHolder pandaHolder){
        String name = pandaHolder.getName();
        Class clazz = pandaHolder.clazz();
        if (pandaHolderByName.containsKey(name)){
            throw new RuntimeException("已经有相同名字的panda");
        }
        synchronized (pandaHolderByName){
            pandaHolderByName.put(name,pandaHolder);
        }
        afterRegisterProcessorAware(pandaHolder);
        relateClassAndName(clazz,name);
    }
    private void relateClassAndName(Class clazz,String name){
        if (!needExclude(clazz)){
            List<String> stringList = pandaNameByClass.get(clazz);
            if (stringList == null){
                stringList = new LinkedList<>();
                stringList.add(name);
                pandaNameByClass.put(clazz,stringList);
            }else {
                stringList.add(name);
            }
            if (!clazz.isInterface()){
                Class superclass = clazz.getSuperclass();
                relateClassAndName(superclass,name);
            }
            Class[] interfaces = clazz.getInterfaces();
            for (Class anInterface : interfaces) {
                relateClassAndName(anInterface,name);
            }
        }

    }
    private boolean needExclude(Class clazz){
        if (clazz.getPackageName().startsWith("java")){
            return true;
        }
        if (clazz == Object.class){
            return true;
        }
        if (clazz == Serializable.class|| clazz == Closeable.class){
            return true;
        }
        if (ClassTool.isSimpleType(clazz)){
            return true;
        }
        return false;
    }
    private void pandaNotice(Object nameOrType, PandaHolder pandaHolder) {
        if (pandaHolder.raw() == null) {
            return;
        }
        synchronized (pandaSubscribers) {
            //避免在forEach时，对它进行add
            Set<Consumer<PandaHolder>> consumerSet = pandaSubscribers.get(nameOrType);
            if (consumerSet != null) {
                consumerSet.forEach(consumer -> {
                    consumer.accept(pandaHolder);
                });
            }
        }
    }
    protected void pandaSubscribe(Object nameOrType, Consumer<PandaHolder> callback) {
        if (nameOrType != null) {
            synchronized (pandaSubscribers) {
                Set<Consumer<PandaHolder>> tmp = pandaSubscribers.get(nameOrType);
                if (tmp == null) {
                    tmp = new LinkedHashSet<>();
                    pandaSubscribers.put(nameOrType, tmp);
                }
                tmp.add(callback);
            }
        }
    }
    public void collectPanda(Class<?> clazz){
        Annotation[] annotations = AnnotationTool.getAnnotations(clazz);
        for (Annotation annotation : annotations) {
            PandaBuilder<Annotation> pandaBuilder = (PandaBuilder<Annotation>) getPandaBuilder(annotation.annotationType());
            if (pandaBuilder != null){
                pandaBuilder.collect(clazz,annotation,this::processPandaEmbryo);
            }
        }
    }

    public List<PandaHolder<Aspect>> getAspectList() {
        return aspectList;
    }

    public void buildPanda(){
        //先构建切面
        for (PandaEmbryo<? extends Annotation> pandaEmbryo : aspectEmbryoList) {
            pandaEmbryo.build(this::processPandaHolder,this);
        }
        for (PandaEmbryo<? extends Annotation> pandaEmbryo : embryoList) {
            pandaEmbryo.build(this::processPandaHolder,this);
        }
    }


    public void processPandaEmbryo(PandaEmbryo pandaEmbryo){
        if (pandaNames.contains(pandaEmbryo.name())){
            throw new RuntimeException("已经有相同名字的panda");
        }
        afterCollectProcessorAware(pandaEmbryo);
        if (Aspect.class.isAssignableFrom(pandaEmbryo.clazz())){
            aspectEmbryoList.add(pandaEmbryo);
        }else {
            embryoList.add(pandaEmbryo);
        }
    }
    public void processPandaHolder(PandaHolder pandaHolder){
        pandaHolder.inject(this);
        beforeRegisterProcessorAware(pandaHolder);
        registerPanda(pandaHolder);
        if (Aspect.class.isAssignableFrom(pandaHolder.clazz())){
            aspectList.add(pandaHolder);
        }
    }
    /**
     * 1给用户一个修改当前注册到容器实例的口子，
     * 2同时提供一个口子可以注册更多实例的口子
     */
    private void afterCollectProcessorAware(PandaEmbryo pandaEmbryo){
        for (AwarenessProcessor awarenessProcessor : awarenessProcessorList) {
            awarenessProcessor.afterCollect(pandaEmbryo,this::processPandaEmbryo);
        }
    }
    /**
     * 在实例注册到容器里之前一些通用的能力处理
     */
    private void beforeRegisterProcessorAware(PandaHolder pandaHolder){
        for (AwarenessProcessor awarenessProcessor : awarenessProcessorList) {
            awarenessProcessor.beforeRegister(pandaHolder,this);
        }
    }

    /**
     * 完成实例在容器注册后的一些通用能力处理
     */
    private void afterRegisterProcessorAware(PandaHolder pandaHolder){
        for (AwarenessProcessor awarenessProcessor : awarenessProcessorList) {
            awarenessProcessor.afterRegister(pandaHolder);
        }
    }

}
